/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.raflik.queryxml.langservice;

import java.util.WeakHashMap;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathException;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactoryConfigurationException;
import pl.raflik.queryxml.lib.exception.NullArgumentException;

/**
 *
 * @author raflik
 */
public abstract class GenericJavaxXPathApiResolver implements QueryLanguage {

    private XPath xpath = null;
    private final WeakHashMap<String, XPathExpression> exprCache = new WeakHashMap<String, XPathExpression>();

    private XPathExpression cacheAwareCompile(String query, boolean addToCache) throws XPathExpressionException, XPathFactoryConfigurationException {
        if (query == null) {
            throw new NullArgumentException("query");
        }

        XPathExpression expr;
        if (exprCache.containsKey(query)) {
            expr = exprCache.get(query);
        } else {
            expr = getXPath().compile(query);
            if (addToCache) {
                exprCache.put(query, expr);
            }
        }
        return expr;
    }

    protected abstract XPath instantiateXPath() throws XPathFactoryConfigurationException;

    protected final XPath getXPath() throws XPathFactoryConfigurationException {
        if (xpath == null) {
            xpath = instantiateXPath();
        }
        return xpath;
    }

    public Object genericResolve(String query, Object node, Object param) throws QueryResolverException {
        if (node == null) {
            throw new NullArgumentException("node");
        }
        if (param != null && !(param instanceof QName)) {
            throw new IllegalArgumentException("param");
        }

        QName returnType = (QName) param;
        returnType = returnType == null ? XPathConstants.NODESET : returnType;

        XPathExpression expr;
        try {
            expr = cacheAwareCompile(query, true);
            return expr.evaluate(node, returnType);
        } catch (XPathException e) {
            throw new InvalidQueryException(e);
        }
    }

    @Override
    public boolean isValid(String query) {
        try {
            cacheAwareCompile(query, true);
            return true;
        } catch (XPathException e) {
        }
        return false;
    }
    
    private static final Object[] params = new Object[0];

    public Object[] getParams() {
        return params;
    }
}
